#include <os/lock.h>
#include <os/sched.h>
#include <os/list.h>
#include <atomic.h>

mutex_lock_t mlocks[LOCK_NUM];

void init_locks(void)
{
    /* TODO: [p2-task2] initialize mlocks */
    int lock_key[1] = {42};
    for(int i = 0;i < 1;i++){
        mlocks[i].key = lock_key[i];

        spin_lock_t* lock = (spin_lock_t*)(&mlocks[i]);
        spin_lock_init(lock);
        
        mlocks[i].block_queue.prev = &mlocks[i].block_queue;
        mlocks[i].block_queue.next = &mlocks[i].block_queue;
    }
}

void spin_lock_init(spin_lock_t *lock)
{
    /* TODO: [p2-task2] initialize spin lock */
    lock->status = UNLOCKED;
}

int spin_lock_try_acquire(spin_lock_t *lock)
{
    /* TODO: [p2-task2] try to acquire spin lock */
    if(lock->status == UNLOCKED){
        return 1;
    }else{
        return 0;
    }
}

void spin_lock_acquire(spin_lock_t *lock)
{
    /* TODO: [p2-task2] acquire spin lock */
    lock->status = LOCKED;
}

void spin_lock_release(spin_lock_t *lock)
{
    /* TODO: [p2-task2] release spin lock */
    lock->status = UNLOCKED;
}

int do_mutex_lock_init(int key)
{
    /* TODO: [p2-task2] initialize mutex lock */
    int i;
    for(i = 0;i < LOCK_NUM;i++){
        if(mlocks[i].key == key){
            break;
        }
    }
    
    return i;
}

void do_mutex_lock_acquire(int mlock_idx)
{
    /* TODO: [p2-task2] acquire mutex lock */
    spin_lock_t* lock = (spin_lock_t*)(&mlocks[mlock_idx]);

    while(!spin_lock_try_acquire(lock)){

        list_node_t* pcb_node = (list_node_t*)(&(current_running->list));
        list_head*   queue    = (list_head*)(&(mlocks[mlock_idx].block_queue));
        do_block(pcb_node,queue);

        do_scheduler();
    }

    spin_lock_acquire(lock);
    return;
}

void do_mutex_lock_release(int mlock_idx)
{
    /* TODO: [p2-task2] release mutex lock */
    spin_lock_t* lock = (spin_lock_t*)(&mlocks[mlock_idx]);
    spin_lock_release(lock);

    //if the block_list is not empty,then do_unblock the node;
    while(mlocks[mlock_idx].block_queue.next != &(mlocks[mlock_idx].block_queue)){
        list_node_t* pcb_node = mlocks[mlock_idx].block_queue.next;
        do_unblock(pcb_node);
    }

    return;


}
